﻿using System.IO;
using gixoom.Dom.Block;

namespace gixoom.Dom.Storage.Block
{
    internal class FilesBlockStorage: IBlockStorage
    {
        private string filesPath;
        private IBlockFacade blockFacade;

        /// <summary>
        /// Creates FilesBlockStorage object.
        /// </summary>
        /// <param name="filesPath">the files path</param>
        /// <param name="blockFacade">block facade</param>
        /// <exception cref="System.ArgumentNullException"><i>blockFacade</i> is a null reference.</exception>
        public FilesBlockStorage(string filesPath, IBlockFacade blockFacade)
        {
            Ensure.ArgumentNotNull(blockFacade, "blockFacade");

            this.filesPath = filesPath;
            this.blockFacade = blockFacade;
        }

        /// <summary>
        /// Stores the specified block.
        /// </summary>
        /// <param name="block">the block</param>
        /// <returns>true if the block was successfully stored</returns>
        /// <exception cref="ArgumentNullException"><i>block</i> is a null reference.</exception>
        public bool StoreBlock(IBlock block)
        {
            Ensure.ArgumentNotNull(block, "block");

            string name = FormFileName(block.Id.BlockType, block.Id.Score.ToString());
            name = FormFullName(filesPath, name);
            FileStream stream = CreateFile(name);
            bool wasStreamOpened = (stream != null);

            if (wasStreamOpened)
            {
                using (stream)
                {
                    byte[] content = block.GetContentCopy();
                    stream.Write(content, 0, content.Length);
                }
            }

            return wasStreamOpened;
        }

        /// <summary>
        /// Retrieves the block by the specified block id.
        /// </summary>
        /// <param name="blockId">the block id</param>
        /// <returns>the block by the specified block id or null if the block cannot be retrieved</returns>
        /// <exception cref="ArgumentNullException"><i>blockId</i> is a null reference.</exception>
        public IBlock RetrieveBlock(IBlockId blockId)
        {
            Ensure.ArgumentNotNull(blockId, "blockId");

            IBlock block = null;
            string name = FormFileName(blockId.BlockType, blockId.Score.ToString());
            name = FormFullName(filesPath, name);

            // TODO: all exceptions should be properly processed
            try
            {
                byte[] content = System.IO.File.ReadAllBytes(name);
                block = blockFacade.CreateBlockFromContent(blockId.BlockType, content);

                if (!blockId.Score.Equals(block.Id.Score))
                {
                    // TODO: the case should be propagated back
                    block = null;
                }
            }
            catch
            {
            }

            return block;
        }

        /// <summary>
        /// Checks whether the block with the specified block id exists.
        /// </summary>
        /// <param name="blockId">the block id to check existence of the block for</param>
        /// <returns>true if the block exists</returns>
        /// <exception cref="System.ArgumentNullException"><i>blockId</i> is a null reference.</exception>
        public bool ExistsBlock(IBlockId blockId)
        {
            Ensure.ArgumentNotNull(blockId, "blockId");

            string name = FormFileName(blockId.BlockType, blockId.Score.ToString());
            name = FormFullName(filesPath, name);

            return System.IO.File.Exists(name);
        }

        private FileStream CreateFile(string name)
        {
            FileStream stream = null;

            // TODO: exceptions should be properly processed
            try
            {
                stream = System.IO.File.Create(name);
            }
            catch
            {
            }

            return stream;
        }

        private string FormFileName(BlockType blockType, string scoreString)
        {
            var blockTypeString = ((byte)blockType).ToString("X2");
            var fileName = blockTypeString + scoreString;

            return fileName;
        }

        private string FormFullName(string path, string fileName)
        {
            string fullName = path[path.Length - 1] == '\\' ?
                path + fileName : path + "\\" + fileName;
            return fullName;
        }
    }
}
